package com.piece.module.flowable.service;

import com.piece.admin.api.dto.SysUserDTO;
import com.piece.admin.model.TbSysDept;
import com.piece.admin.model.TbSysRole;
import com.piece.admin.model.TbSysUser;
import com.piece.admin.service.TbSysDeptService;
import com.piece.admin.service.TbSysRoleService;
import com.piece.admin.service.TbSysUserService;
import com.piece.admin.support.UserInfoSupport;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.file.IoUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.log.config.UserContextHolder;
import com.piece.module.flowable.constants.ExceptionConstants;
import com.piece.module.flowable.constants.ProcessConstants;
import com.piece.module.flowable.constants.TaskConstants;
import com.piece.module.flowable.dto.*;
import com.piece.module.flowable.enums.FlowComment;
import com.piece.module.flowable.factory.FlowServiceFactory;
import com.piece.module.flowable.flow.CustomProcessDiagramGenerator;
import com.piece.module.flowable.model.WfDeployForm;
import com.piece.module.flowable.repository.WfDeployFormRepository;
import com.piece.module.flowable.util.FlowUtil;
import com.piece.module.flowable.util.UserTaskUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程任务Service
 */
@Service
@Transactional
public class WfTaskService extends FlowServiceFactory {

    @Autowired
    private WfDeployFormRepository wfDeployFormRepository;

    @Resource
    private WfCopyService wfCopyService;

    @Resource
    private TbSysUserService tbSysUserService;

    @Resource
    private TbSysRoleService tbSysRoleService;

    @Resource
    private TbSysDeptService tbSysDeptService;

    public DataTable<WfTask> findPage(QueryTable queryTable) {
        DataTable dataTable = new DataTable(queryTable);
        Map<String, Object> params = queryTable.getParams();

        HistoricProcessInstanceQuery historicProcessInstanceQuery = null;
        String starter = Convert.toStr(params.get("Starter"));
        if (StringUtil.isNotEmpty(starter)) {
            historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                    .startedBy(starter)
                    .orderByProcessInstanceStartTime()
                    .desc();
        } else {
            historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                    .orderByProcessInstanceStartTime()
                    .desc();
        }

        if (ObjectUtil.isNotEmpty(params)) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            String paramName;
            for (Map.Entry<String, Object> entry : entries) {
                paramName = entry.getKey();
                if (paramName.startsWith("Q_") && ObjectUtil.isNotEmpty(entry.getValue())) {
                    paramName = paramName.split("_")[1];
                    switch (paramName) {
                        case "processName":
                            historicProcessInstanceQuery.processInstanceNameLike("%" + Convert.toStr(entry.getValue()) + "%");
                            break;
                        case "category":
                            historicProcessInstanceQuery.processDefinitionCategory(Convert.toStr(entry.getValue()));
                            break;
                        case "deployTime":
                            historicProcessInstanceQuery.startedAfter(Convert.toDate(entry.getValue()));
                            break;
                    }
                }
            }
        }

        long totalCount = historicProcessInstanceQuery.count();
        int offset = queryTable.getLength() * (queryTable.getCurrentPage() - 1);
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery
                .listPage(offset, queryTable.getLength());

        List<WfTask> taskList = historicProcessInstances.stream().map(historic -> {
            WfTask vo = new WfTask();
            vo.setCreateTime(historic.getStartTime());
            vo.setFinishTime(historic.getEndTime());
            vo.setProcInsId(historic.getId());
            // 计算耗时
            if (Objects.nonNull(historic.getEndTime())) {
                vo.setDuration(DateUtil.diff(historic.getEndTime(), historic.getStartTime()));
            } else {
                vo.setDuration(DateUtil.diff(new Date(), historic.getStartTime()));
            }

            // 流程定义信息
            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(historic.getProcessDefinitionId())
                    .singleResult();
            vo.setDeployId(definition.getDeploymentId());
            vo.setProcDefId(definition.getId());
            vo.setProcDefKey(definition.getKey());
            vo.setProcDefName(definition.getName());
            vo.setProcDefVersion(definition.getVersion());
            vo.setCategory(definition.getCategory());
            vo.setProcDefVersion(definition.getVersion());

            // 当前所处流程
            List<Task> list = taskService.createTaskQuery().processInstanceId(historic.getId()).list();
            if (ObjectUtil.isNotEmpty(list)) {
                vo.setTaskId(list.get(0).getId());
                vo.setTaskName(list.get(0).getName());
                if (StringUtil.isNotEmpty(list.get(0).getAssignee())) {
                    TbSysUser assign = tbSysUserService.findById(Convert.toLong(list.get(0).getAssignee()));
                    if (null != assign) {
                        UserInfoSupport.setOtherInfo(assign);
                        vo.setAssigneeId(assign.getUserId());
                        vo.setAssigneeName(assign.getUserName());
                        vo.setDeptName(assign.getDeptName());
                    }
                }
            } else {
                List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                        .processInstanceId(historic.getId()).orderByHistoricTaskInstanceEndTime().desc().list();
                if (ObjectUtil.isNotEmpty(historicTaskInstance)) {
                    vo.setTaskId(historicTaskInstance.get(0).getId());
                    vo.setTaskName(historicTaskInstance.get(0).getName());
                    if (StringUtil.isNotEmpty(historicTaskInstance.get(0).getAssignee())) {
                        TbSysUser assign = tbSysUserService.findById(Convert.toLong(historicTaskInstance.get(0).getAssignee()));
                        if (null != assign) {
                            UserInfoSupport.setOtherInfo(assign);
                            vo.setAssigneeId(assign.getUserId());
                            vo.setAssigneeName(assign.getUserName());
                            vo.setDeptName(assign.getDeptName());
                        }
                    }
                }
            }

            // 当前流程状态
            if (null == vo.getFinishTime()) {
                vo.setState(FlowComment.RUNNING.getRemark());
            } else {
                vo.setState(FlowComment.FINISH.getRemark());
            }

            // 获取意见评论内容
            List<Comment> commentList = taskService.getProcessInstanceComments(historic.getId());
            if (ObjectUtil.isNotEmpty(commentList)) {
                List<Comment> comments = commentList.stream()
                        .filter(comment -> {
                            if (FlowComment.CANCEL.getType().equals(comment.getType())) {
                                vo.setState(FlowComment.CANCEL.getRemark());
                            }
                            return comment.getProcessInstanceId().equals(historic.getId());
                        }).collect(Collectors.toList());
                vo.setCommentList(comments);
            }

            return vo;
        }).collect(Collectors.toList());

        dataTable.setData(taskList);
        dataTable.setRecordsTotal(Convert.toInt(totalCount));
        return dataTable;
    }

    /**
     * 查询代签任务列表
     */
    public DataTable<WfTask> findClaimPage(QueryTable queryTable) {
        DataTable dataTable = new DataTable(queryTable);
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateUser(UserContextHolder.getUserId())
                .taskCandidateGroupIn(UserTaskUtil.getCandidateGroup())
                .orderByTaskCreateTime().desc();

        Map<String, Object> params = queryTable.getParams();
        if (ObjectUtil.isNotEmpty(params)) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            String paramName;
            for (Map.Entry<String, Object> entry : entries) {
                paramName = entry.getKey();
                if (paramName.startsWith("Q_") && ObjectUtil.isNotEmpty(entry.getValue())) {
                    paramName = paramName.split("_")[1];
                    switch (paramName) {
                        case "processName":
                            taskQuery.processDefinitionNameLike("%" + Convert.toStr(entry.getValue()) + "%");
                            break;
                        case "taskName":
                            taskQuery.taskNameLike("%" + Convert.toStr(entry.getValue()) + "%");
                            break;
                        case "category":
                            taskQuery.processCategoryIn(Convert.toStrList(entry.getValue().toString()));
                            break;
                    }
                }
            }
        }

        long totalCount = taskQuery.count();
        int offset = queryTable.getLength() * (queryTable.getCurrentPage() - 1);

        List<Task> taskList = taskQuery.listPage(offset, queryTable.getLength());
        List<WfTask> wfTaskList = taskList.stream().map(task -> buildTask(task)).collect(Collectors.toList());
        dataTable.setData(wfTaskList);
        dataTable.setRecordsTotal(Convert.toInt(totalCount));
        return dataTable;
    }

    private WfTask buildTask(Task task) {
        WfTask wfTask = new WfTask();
        // 当前流程信息
        wfTask.setTaskId(task.getId());
        wfTask.setProcDefId(task.getProcessDefinitionId());
        wfTask.setTaskDefKey(task.getTaskDefinitionKey());
        wfTask.setCreateTime(task.getCreateTime());
        wfTask.setTaskName(task.getName());

        // 流程定义信息
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(task.getProcessDefinitionId())
                .singleResult();
        wfTask.setDeployId(pd.getDeploymentId());
        wfTask.setProcDefName(pd.getName());
        wfTask.setProcDefId(pd.getId());
        wfTask.setProcDefVersion(pd.getVersion());
        wfTask.setProcInsId(task.getProcessInstanceId());
        wfTask.setCategory(pd.getCategory());

        // 流程发起人信息
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        TbSysUser startUser = tbSysUserService.findById(Convert.toLong(historicProcessInstance.getStartUserId()));
        UserInfoSupport.setOtherInfo(startUser);
        wfTask.setStartUserId(startUser.getUserId() + "");
        wfTask.setStartUserName(startUser.getUserName());
        wfTask.setStartDeptName(startUser.getDeptName());
        return wfTask;
    }

    /**
     * 查询已完成任务列表
     */
    public DataTable<WfTask> findFinishedPage(QueryTable queryTable) {
        DataTable dataTable = new DataTable(queryTable);
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskAssignee(UserContextHolder.getUserId())
                .orderByHistoricTaskInstanceEndTime()
                .desc();

        long totalCount = taskInstanceQuery.count();
        int offset = queryTable.getLength() * (queryTable.getCurrentPage() - 1);

        List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(offset, queryTable.getLength());
        List<WfTask> hisTaskList = historicTaskInstanceList.stream().map(historic -> {
            WfTask vo = new WfTask();
            // 当前流程信息
            vo.setTaskId(historic.getId());
            // 审批人员信息
            if (StringUtil.isNotEmpty(historic.getAssignee())) {
                TbSysUser assign = tbSysUserService.findById(Convert.toLong(historic.getAssignee()));
                if (null != assign) {
                    UserInfoSupport.setOtherInfo(assign);
                    vo.setAssigneeId(assign.getUserId());
                    vo.setAssigneeName(assign.getUserName());
                    vo.setDeptName(assign.getDeptName());
                }
            }
            vo.setCreateTime(historic.getCreateTime());
            vo.setFinishTime(historic.getEndTime());
            vo.setDuration(DateUtil.getDescriptionTime(historic.getDurationInMillis()));
            vo.setProcDefId(historic.getProcessDefinitionId());
            vo.setTaskDefKey(historic.getTaskDefinitionKey());
            vo.setTaskName(historic.getName());

            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(historic.getProcessDefinitionId())
                    .singleResult();
            vo.setDeployId(pd.getDeploymentId());
            vo.setProcDefName(pd.getName());
            vo.setProcDefVersion(pd.getVersion());
            vo.setProcInsId(historic.getProcessInstanceId());
            vo.setHisProcInsId(historic.getProcessInstanceId());
            vo.setCategory(pd.getCategory());

            // 流程发起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(historic.getProcessInstanceId())
                    .singleResult();
            TbSysUser startUser = tbSysUserService.findById(Convert.toLong(historic.getAssignee()));
            if (null != startUser) {
                UserInfoSupport.setOtherInfo(startUser);
                vo.setStartUserId(startUser.getUserId() + "");
                vo.setStartUserName(startUser.getUserName());
                vo.setStartDeptName(startUser.getDeptName());
            }

            // 流程变量
            vo.setVariables(this.getProcessVariables(historic.getId()));
            return vo;
        }).collect(Collectors.toList());

        dataTable.setData(hisTaskList);
        dataTable.setRecordsTotal(Convert.toInt(totalCount));
        return dataTable;
    }

    /**
     * 查询代办任务列表
     */
    public DataTable<WfTask> findTodoPage(QueryTable queryTable) {
        DataTable dataTable = new DataTable(queryTable);

        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateOrAssigned(UserContextHolder.getUserId())
                .taskCandidateGroupIn(UserTaskUtil.getCandidateGroup())
                .orderByTaskCreateTime().desc();

        Map<String, Object> params = queryTable.getParams();
        if (ObjectUtil.isNotEmpty(params)) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            String paramName;
            for (Map.Entry<String, Object> entry : entries) {
                paramName = entry.getKey();
                if (paramName.startsWith("Q_") && ObjectUtil.isNotEmpty(entry.getValue())) {
                    paramName = paramName.split("_")[1];
                    switch (paramName) {
                        case "taskName":
                            taskQuery.taskNameLike("%" + Convert.toStr(entry.getValue()) + "%");
                            break;
                        case "category":
                            taskQuery.processCategoryIn(Convert.toStrList(entry.getValue().toString()));
                            break;
                    }
                }
            }
        }

        long totalCount = taskQuery.count();
        int offset = queryTable.getLength() * (queryTable.getCurrentPage() - 1);

        List<Task> taskList = taskQuery.listPage(offset, queryTable.getLength());
        List<WfTask> wfTaskList = taskList.stream().map(task -> {
            WfTask wfTask = buildTask(task);
            // 流程变量
            wfTask.setVariables(this.getProcessVariables(task.getId()));
            return wfTask;
        }).collect(Collectors.toList());

        dataTable.setData(wfTaskList);
        dataTable.setRecordsTotal(Convert.toInt(totalCount));
        return dataTable;
    }

    /**
     * 查询待办任务
     *
     * @param involved 候选人范围和候选组范围
     * @param count    查询数量
     */
    public List<WfTask> findTodoList(String involved, int count) {
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateOrAssigned(UserContextHolder.getUserId())
                //.taskCandidateGroupIn(UserTaskUtil.getCandidateGroup())
                .orderByTaskCreateTime().desc();
        List<Task> taskList = taskQuery.listPage(0, count);
        return taskList.stream().map(task -> {
            WfTask wfTask = buildTask(task);
            wfTask.setVariables(this.getProcessVariables(task.getId()));
            return wfTask;
        }).collect(Collectors.toList());
    }

    /**
     * 查询待办任务数量
     *
     * @param involved 候选人范围和候选组范围
     */
    public long countTodoProcess(String involved) {
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .taskCandidateOrAssigned(UserContextHolder.getUserId())
                //.taskCandidateGroupIn(UserTaskUtil.getCandidateGroup())
                .orderByTaskCreateTime().desc();
        return taskQuery.count();
    }

    /**
     * 获取表单内容
     */
    public String getFormContent(String definitionId, String deployId) {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        String bpmnString = null;
        try {
            bpmnString = IoUtil.toString(inputStream, StandardCharsets.UTF_8.name());
        } catch (IOException e) {
            throw new ServerException(ExceptionConstants.DESIGN_ERROR, null);
        }
        BpmnModel bpmnModel = FlowUtil.getBpmnModel(bpmnString);
        StartEvent startEvent = FlowUtil.getStartEvent(bpmnModel);
        WfDeployForm deployForm = wfDeployFormRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("deployId", deployId)
                .put("formKey", startEvent.getFormKey())
                .put("nodeKey", startEvent.getId())
                .build());
        if (ObjectUtil.isNull(deployForm)) {
            throw new ServerException(ExceptionConstants.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
        }
        return deployForm.getContent();
    }

    /**
     * 流程详情信息
     *
     * @param procInsId 流程实例ID
     * @param deployId  流程部署ID
     * @param taskId    任务ID
     */
    public WfDetail queryProcessDetail(String procInsId, String deployId, String taskId) {
        WfDetail detail = new WfDetail();
        HistoricTaskInstance taskIns = historyService.createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .includeIdentityLinks()
                .includeProcessVariables()
                .includeTaskLocalVariables()
                .singleResult();
        if (null == taskIns) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "Task_" + taskId);
        }
        detail.setTaskFormData(currTaskFormData(deployId, taskIns));
        detail.setHistoryTaskList(historyTaskList(procInsId));
        detail.setProcessFormList(processFormList(procInsId, deployId, taskIns));
        detail.setVariables(getProcessVariables(taskId));
        return detail;
    }

    /**
     * 获取当前任务流程表单信息
     */
    private FormConf currTaskFormData(String deployId, HistoricTaskInstance instance) {
        WfDeployForm deployForm = wfDeployFormRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("deployId", deployId)
                .put("formKey", instance.getFormKey())
                .put("nodeKey", instance.getTaskDefinitionKey())
                .build());

        if (ObjectUtil.isNotEmpty(deployForm)) {
            FormConf currTaskFormData = JsonUtil.toBean(deployForm.getContent(), FormConf.class);
            if (null != currTaskFormData) {
                currTaskFormData.setFormBtns(false);
                FlowUtil.fillFormData(currTaskFormData, instance.getProcessVariables());
                return currTaskFormData;
            }
        }
        return null;
    }

    private List<WfTask> historyTaskList(String procInsId) {
        Set<String> activityTypes = new HashSet<>();
        activityTypes.add(BpmnXMLConstants.ELEMENT_EVENT_START);
        activityTypes.add(BpmnXMLConstants.ELEMENT_TASK_USER);
        activityTypes.add(BpmnXMLConstants.ELEMENT_EVENT_END);
        List<HistoricActivityInstance> taskInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId)
                .activityTypes(activityTypes)
                .orderByHistoricActivityInstanceStartTime().asc()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(procInsId)
                .singleResult();

        List<Comment> commentList = taskService.getProcessInstanceComments(procInsId);
        List<WfTask> taskList = taskInstanceList.stream().map(instance -> {
            WfTask vo = new WfTask();
            vo.setProcDefId(instance.getProcessDefinitionId());
            vo.setTaskId(instance.getId());
            vo.setTaskDefKey(instance.getActivityId());
            vo.setTaskName(instance.getActivityName());
            vo.setCreateTime(instance.getStartTime());
            vo.setFinishTime(instance.getEndTime());
            if (ObjectUtil.isNotNull(instance.getDurationInMillis())) {
                vo.setDuration(DateUtil.getDescriptionTime(instance.getDurationInMillis()));
            }

            if (BpmnXMLConstants.ELEMENT_EVENT_START.equals(instance.getActivityType())) {
                // 展示发起人
                if (ObjectUtil.isNotNull(historicProcessInstance)) {
                    TbSysUser startUser = tbSysUserService.findById(Convert.toLong(historicProcessInstance.getStartUserId()));
                    if (null != startUser) {
                        UserInfoSupport.setOtherInfo(startUser);
                        vo.setStartUserId(startUser.getUserId() + "");
                        vo.setStartUserName(startUser.getUserName());
                        vo.setStartDeptName(startUser.getDeptName());
                    }
                }
            } else if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(instance.getActivityType())) {
                if (ObjectUtil.isNotNull(instance.getEndTime())) {
                    // 展示处理人
                    if (StringUtil.isNotEmpty(instance.getAssignee())) {
                        TbSysUser user = tbSysUserService.findById(Convert.toLong(instance.getAssignee()));
                        if (null != user) {
                            UserInfoSupport.setOtherInfo(user);
                            vo.setAssigneeId(user.getUserId());
                            vo.setAssigneeName(user.getUserName());
                            vo.setDeptName(user.getDeptName());
                        }
                    }
                } else {
                    // 展示待办人员
                    StringBuilder stringBuilder = new StringBuilder();
                    List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(instance.getTaskId());
                    for (HistoricIdentityLink identityLink : linksForTask) {
                        if ("candidate".equals(identityLink.getType())) {
                            if (StringUtil.isNotBlank(identityLink.getUserId())) {
                                TbSysUser user = tbSysUserService.findById(Convert.toLong(identityLink.getUserId()));
                                if (null != user) {
                                    stringBuilder.append(user.getUserName()).append(",");
                                }
                            }
                            if (StringUtil.isNotBlank(identityLink.getGroupId())) {
                                if (identityLink.getGroupId().startsWith(TaskConstants.ROLE_GROUP_PREFIX)) {
                                    Long roleId = Convert.toLong(StringUtil.stripStart(identityLink.getGroupId(), TaskConstants.ROLE_GROUP_PREFIX));
                                    TbSysRole role = tbSysRoleService.findById(roleId);
                                    stringBuilder.append(role.getRoleName()).append(",");
                                } else if (identityLink.getGroupId().startsWith(TaskConstants.DEPT_GROUP_PREFIX)) {
                                    Long deptId = Convert.toLong(StringUtil.stripStart(identityLink.getGroupId(), TaskConstants.DEPT_GROUP_PREFIX));
                                    TbSysDept dept = tbSysDeptService.findById(deptId);
                                    stringBuilder.append(dept.getDeptName()).append(",");
                                }
                            }
                        } else if ("assignee".equals(identityLink.getType())) {
                            if (StringUtil.isNotBlank(identityLink.getUserId())) {
                                TbSysUser user = tbSysUserService.findById(Convert.toLong(identityLink.getUserId()));
                                stringBuilder.append(user.getUserName()).append(",");
                            }
                        }
                    }

                    if (StringUtil.isNotBlank(stringBuilder)) {
                        vo.setCandidateUsers(stringBuilder.substring(0, stringBuilder.length() - 1));
                    }
                }
            }

            // 当前流程状态
            if (null == vo.getFinishTime()) {
                vo.setState(FlowComment.RUNNING.getRemark());
            } else {
                vo.setState(FlowComment.FINISH.getRemark());
            }

            // 获取意见评论内容
            if (ObjectUtil.isNotEmpty(commentList)) {
                List<Comment> comments = commentList.stream()
                        .filter(comment -> {
                            if (FlowComment.CANCEL.getType().equals(comment.getType())) {
                                vo.setState(FlowComment.CANCEL.getRemark());
                            }
                            return comment.getTaskId().equals(instance.getId());
                        }).collect(Collectors.toList());
                vo.setCommentList(comments);
            }
            return vo;
        }).collect(Collectors.toList());

        return taskList;
    }

    /**
     * 获取流程表单信息（不包括当前任务节点）
     */
    private List<FormConf> processFormList(String procInsId, String deployId, HistoricTaskInstance instance) {
        List<FormConf> procFormList = new ArrayList<>();
        HistoricProcessInstance historicProcIns = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).includeProcessVariables().singleResult();
        Process process = repositoryService.getBpmnModel(historicProcIns.getProcessDefinitionId()).getMainProcess();

        buildStartFormData(historicProcIns, process, deployId, procFormList);
        buildUserTaskFormData(procInsId, deployId, process, procFormList);
        return procFormList;
    }

    private void buildStartFormData(HistoricProcessInstance historicProcIns, Process process, String deployId, List<FormConf> procFormList) {
        procFormList = null == procFormList ? new ArrayList<>() : procFormList;
        HistoricActivityInstance startInstance = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(historicProcIns.getId())
                .activityId(historicProcIns.getStartActivityId())
                .singleResult();
        StartEvent startEvent = (StartEvent) process.getFlowElement(startInstance.getActivityId());
        WfDeployForm startFormInfo = wfDeployFormRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("deployId", deployId)
                .put("formKey", startEvent.getFormKey())
                .put("nodeKey", startEvent.getId())
                .build());
        if (ObjectUtil.isNotNull(startFormInfo)) {
            FormConf formConf = JsonUtil.toBean(startFormInfo.getContent(), FormConf.class);
            if (null != formConf) {
                formConf.setTitle(startEvent.getName());
                formConf.setDisabled(true);
                formConf.setFormBtns(false);
                FlowUtil.fillFormData(formConf, historicProcIns.getProcessVariables());
                procFormList.add(formConf);
            }
        }
    }

    private void buildUserTaskFormData(String procInsId, String deployId, Process process, List<FormConf> procFormList) {
        procFormList = null == procFormList ? new ArrayList<>() : procFormList;
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId).finished()
                .activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        for (HistoricActivityInstance instanceItem : activityInstanceList) {
            UserTask userTask = (UserTask) process.getFlowElement(instanceItem.getActivityId(), true);
            String formKey = userTask.getFormKey();
            if (null == formKey) {
                continue;
            }
            // 查询任务节点参数，并转换成Map
            Map<String, Object> variables = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(procInsId)
                    .taskId(instanceItem.getTaskId())
                    .list()
                    .stream()
                    .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));
            WfDeployForm deployFormInfo = wfDeployFormRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                    .put("deployId", deployId)
                    .put("formKey", formKey)
                    .put("nodeKey", userTask.getId())
                    .build());
            if (ObjectUtil.isNotNull(deployFormInfo)) {
                FormConf formConf = JsonUtil.toBean(deployFormInfo.getContent(), FormConf.class);
                if (null != formConf) {
                    formConf.setTitle(userTask.getName());
                    formConf.setDisabled(true);
                    formConf.setFormBtns(false);
                    FlowUtil.fillFormData(formConf, variables);
                    procFormList.add(formConf);
                }
            }
        }
    }

    /**
     * 获取流程变量
     */
    public Map<String, Object> getProcessVariables(String taskId) {
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskId(taskId)
                .singleResult();
        if (Objects.nonNull(historicTaskInstance)) {
            return historicTaskInstance.getProcessVariables();
        }
        return taskService.getVariables(taskId);
    }

    /**
     * 获取下一节点人员信息
     */
    public WfNextNode getNextFlowNode(String deployId, String taskId) {
        WfNextNode nextNode = new WfNextNode();
        List<UserTask> nextUserTask = null;
        if (StringUtil.isNotEmpty(deployId)) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
            nextUserTask = UserTaskUtil.getNextUserTasks(repositoryService, processDefinition, MapUtil.EMPTY_MAP);
        } else if (StringUtil.isNotEmpty(taskId)) {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (Objects.nonNull(task)) {
                Map<String, Object> variables = taskService.getVariables(task.getId());
                nextUserTask = UserTaskUtil.getNextUserTasks(repositoryService, task, variables);
            }
        }

        if (CollectionUtil.isNotEmpty(nextUserTask)) {
            List<SysUserDTO> userList = new ArrayList<>();
            Long deptId = Convert.toLong(UserContextHolder.getDeptId());
            for (UserTask userTask : nextUserTask) {
                String dataType = userTask.getAttributeValue(ProcessConstants.NAMESPACE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
                if (Objects.nonNull(multiInstance)) {// 会签节点
                    nextNode.setVars(ProcessConstants.PROCESS_MULTI_INSTANCE_USER);
                    nextNode.setType(ProcessConstants.PROCESS_MULTI_INSTANCE);
                } else {// 自定义节点属性
                    userList = UserTaskUtil.getFlowCandidate(userTask, dataType);
                    nextNode.setType(dataType);
                    nextNode.setVars(ProcessConstants.PROCESS_APPROVAL);
                }

                nextNode.setUserList(userList);
            }
        } else {
            return null;
        }

        return nextNode;
    }

    /**
     * 获取流程执行过程
     */
    public WfViewer getFlowViewer(String procInsId) {
        // 构建查询条件
        HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId);
        List<HistoricActivityInstance> allActivityInstanceList = query.list();
        if (CollectionUtil.isAnyEmpty(allActivityInstanceList)) {
            return new WfViewer();
        }
        // 获取流程发布Id信息
        String processDefinitionId = allActivityInstanceList.get(0).getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        // 查询所有已完成的元素
        List<HistoricActivityInstance> finishedElementList = allActivityInstanceList.stream()
                .filter(item -> ObjectUtil.isNotNull(item.getEndTime())).collect(Collectors.toList());
        // 所有已完成的连线
        Set<String> finishedSequenceFlowSet = new HashSet<>();
        // 所有已完成的任务节点
        Set<String> finishedTaskSet = new HashSet<>();
        finishedElementList.forEach(item -> {
            if (BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW.equals(item.getActivityType())) {
                finishedSequenceFlowSet.add(item.getActivityId());
            } else {
                finishedTaskSet.add(item.getActivityId());
            }
        });
        // 查询所有未结束的节点
        Set<String> unfinishedTaskSet = allActivityInstanceList.stream()
                .filter(item -> ObjectUtil.isNull(item.getEndTime()))
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toSet());
        // DFS 查询未通过的元素集合
        Set<String> rejectedSet = FlowUtil.dfsFindRejects(bpmnModel, unfinishedTaskSet, finishedSequenceFlowSet, finishedTaskSet);
        return new WfViewer(finishedTaskSet, finishedSequenceFlowSet, unfinishedTaskSet, rejectedSet);
    }

    /**
     * 启动流程实例任务
     *
     * @param procDefId  流程定义Id
     * @param procDefKey 流程定义Key
     * @param variables  流程变量
     */
    public void startTask(String procDefId, String procDefKey, Map<String, Object> variables) {
        ProcessDefinition processDefinition = null;
        if (StringUtil.isNotEmpty(procDefId)) {
            processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(procDefId).singleResult();
        } else if (StringUtil.isNotEmpty(procDefKey)) {
            processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(procDefKey).latestVersion().singleResult();
        }
        if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) {
            throw new ServerException(ExceptionConstants.PROCESS_DEFINITION_IS_SUSPENDED, null);
        }

        // 设置流程发起人Id到流程中
        identityService.setAuthenticatedUserId(UserContextHolder.getUserId());
        variables.put(TaskConstants.PROCESS_INITIATOR, UserContextHolder.getUserId());

        ProcessInstance processInstance = null;
        if (StringUtil.isNotEmpty(procDefId)) {
            processInstance = runtimeService.startProcessInstanceById(procDefId, variables);
        } else {
            processInstance = runtimeService.startProcessInstanceByKey(procDefKey, variables);
        }

        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
        if (CollectionUtil.isNotEmpty(tasks)) {
            String userIdStr = (String) variables.get(TaskConstants.PROCESS_INITIATOR);
            for (Task task : tasks) {
                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(),
                        UserContextHolder.getDeptName() + "【" + UserContextHolder.getUserName() + "】发起流程申请");
            }
        }
    }

    /**
     * 完成任务
     */
    public void completeTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            taskService.addComment(wfTask.getTaskId(), wfTask.getProcInsId(), FlowComment.DELEGATE.getType(), wfTask.getComment());
            taskService.resolveTask(wfTask.getTaskId());
        } else {
            taskService.addComment(wfTask.getTaskId(), wfTask.getProcInsId(), FlowComment.NORMAL.getType(), wfTask.getComment());
            taskService.setAssignee(wfTask.getTaskId(), UserContextHolder.getUserId());
            if (ObjectUtil.isNotEmpty(wfTask.getVariables())) {
                taskService.complete(wfTask.getTaskId(), wfTask.getVariables(), true);
            } else {
                taskService.complete(wfTask.getTaskId());
            }
        }

        // 设置任务节点名称
        wfTask.setTaskName(task.getName());
        // 处理抄送用户
        if (!wfCopyService.makeCopy(wfTask)) {
            throw new RuntimeException("抄送任务失败");
        }
    }

    /**
     * 驳回任务
     */
    public void rejectTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }
        if (task.isSuspended()) {
            throw new ServerException(ExceptionConstants.PROCESS_DEFINITION_IS_SUSPENDED, null);
        }

        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowUtil.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        if (ObjectUtil.isNotEmpty(allElements)) {
            for (FlowElement flowElement : allElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // 获取节点信息
                    source = flowElement;
                }
            }
        }

        // 目的获取所有跳转到的节点 targetIds
        // 获取当前节点的所有父级用户任务节点
        // 深度优先算法思想：延边迭代深入
        List<UserTask> parentUserTaskList = FlowUtil.iteratorFindParentUserTasks(source, null, null);
        if (null == parentUserTaskList || parentUserTaskList.size() == 0) {
            throw new ServerException(ExceptionConstants.PROCESS_CANNOT_REJECT, null);
        }

        // 获取活动 ID 即节点 Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
        List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
        // 数据清洗，将回滚导致的脏数据清洗掉
        List<String> lastHistoricTaskInstanceList = FlowUtil.historicTaskInstanceClean(allElements, historicTaskInstanceList);
        // 此时历史任务实例为倒序，获取最后走的节点
        List<String> targetIds = new ArrayList<>();
        // 循环结束标识，遇到当前目标节点的次数
        int number = 0;
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number++;
            }
            // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
            // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
            // number == 1，第一次遇到当前节点
            // number == 2，第二次遇到，代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);
            }
        }

        // 目的获取所有需要被跳转的节点 currentIds
        // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // 需驳回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowUtil.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));

        // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
            throw new ServerException(ExceptionConstants.PROCESS_CANNOT_BACK, null);
        }

        // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));

        // 设置驳回意见
        taskService.setAssignee(task.getId(), UserContextHolder.getUserId());
        currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), wfTask.getComment()));

        try {
            // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId()).
                        moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
            }
            // 如果父级任务只有一个，因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
            }
        } catch (FlowableObjectNotFoundException e) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "流程实例-" + task.getProcessInstanceId());
        } catch (FlowableException e) {
            throw new RuntimeException("无法取消或开始活动");
        }

        // 设置任务节点名称
        wfTask.setTaskName(task.getName());
        if (!wfCopyService.makeCopy(wfTask)) {
            throw new RuntimeException("抄送任务失败");
        }
    }

    /**
     * 退回任务
     */
    public void rollbackTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }
        if (task.isSuspended()) {
            throw new ServerException(ExceptionConstants.PROCESS_DEFINITION_IS_SUSPENDED, null);
        }

        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowUtil.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        // 获取跳转的节点元素
        FlowElement target = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 当前任务节点元素
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = flowElement;
                }
                // 跳转的节点元素
                if (flowElement.getId().equals(wfTask.getTargetKey())) {
                    target = flowElement;
                }
            }
        }

        // 从当前节点向前扫描
        // 如果存在路线上不存在目标节点，说明目标节点是在网关上或非同一路线上，不可跳转
        // 否则目标节点相对于当前节点，属于串行
        Boolean isSequential = FlowUtil.iteratorCheckSequentialReferTarget(source, wfTask.getTargetKey(), null, null);
        if (!isSequential) {
            throw new ServerException(ExceptionConstants.PROCESS_CANNOT_BACK, null);
        }

        // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // 需退回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowUtil.iteratorFindChildUserTasks(target, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));

        // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // 设置回退意见
        taskService.setAssignee(task.getId(), UserContextHolder.getUserId());
        for (String currentTaskId : currentTaskIds) {
            taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.ROLLBACK.getType(), wfTask.getComment());
        }

        try {
            // 当前要跳转的节点列表(1或多)，targetKey 跳转到的节点(1)
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(task.getProcessInstanceId())
                    .moveActivityIdsToSingleActivityId(currentIds, wfTask.getTargetKey()).changeState();
        } catch (FlowableObjectNotFoundException e) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "流程实例-" + task.getProcessInstanceId());
        } catch (FlowableException e) {
            throw new RuntimeException("无法取消或开始活动");
        }

        // 设置任务节点名称
        wfTask.setTaskName(task.getName());
        if (!wfCopyService.makeCopy(wfTask)) {
            throw new RuntimeException("抄送任务失败");
        }
    }

    /**
     * 获取所有可回退的节点
     */
    public List<UserTask> findRollbackTaskList(String taskId, String taskName) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + taskName);
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // 获取当前任务节点元素
        UserTask source = null;
        if (null != flowElements) {
            for (FlowElement flowElement : flowElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                }
            }
        }
        // 获取节点的所有路线
        List<List<UserTask>> roads = FlowUtil.findRoad(source, null, null, null);
        // 可回退的节点列表
        List<UserTask> userTaskList = new ArrayList<>();
        for (List<UserTask> road : roads) {
            if (userTaskList.size() == 0) {
                // 还没有可回退节点直接添加
                userTaskList = road;
            } else {
                // 如果已有回退节点，则比对取交集部分
                userTaskList.retainAll(road);
            }
        }
        return userTaskList;
    }

    /**
     * 认领/签收任务
     */
    public void claimTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }
        taskService.claim(wfTask.getTaskId(), UserContextHolder.getUserId());
    }

    /**
     * 取消认领/签收任务
     */
    public void unClaimTask(WfTask wfTask) {
        taskService.unclaim(wfTask.getTaskId());
    }

    /**
     * 委派任务
     */
    public void delegateTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }

        StringBuilder commentBuilder = new StringBuilder(UserContextHolder.getUserName())
                .append(FlowComment.DELEGATE.getRemark());
        TbSysUser user = tbSysUserService.findById(Convert.toLong(wfTask.getClaimUser()));
        if (ObjectUtil.isNotNull(user)) {
            commentBuilder.append(user.getUserName());
        } else {
            commentBuilder.append(wfTask.getClaimUser());
        }
        if (StringUtil.isNotBlank(wfTask.getComment())) {
            commentBuilder.append(": ").append(wfTask.getComment());
        }

        // 添加审批意见
        taskService.addComment(wfTask.getTaskId(), task.getProcessInstanceId(), FlowComment.DELEGATE.getType(), commentBuilder.toString());
        // 设置办理人为当前登录人
        taskService.setOwner(wfTask.getTaskId(), UserContextHolder.getUserId());
        // 执行委派
        taskService.delegateTask(wfTask.getTaskId(), Convert.toStr(wfTask.getClaimUser()));
        // 设置任务节点名称
        wfTask.setTaskName(task.getName());
        // 处理抄送用户
        if (!wfCopyService.makeCopy(wfTask)) {
            throw new RuntimeException("抄送任务失败");
        }
    }

    /**
     * 转办任务
     */
    public void transferTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().taskId(wfTask.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "任务-" + wfTask.getTaskName());
        }

        StringBuilder commentBuilder = new StringBuilder(UserContextHolder.getUserName())
                .append(FlowComment.TRANSFER.getRemark());
        TbSysUser user = tbSysUserService.findById(Convert.toLong(wfTask.getClaimUser()));
        if (ObjectUtil.isNotNull(user)) {
            commentBuilder.append(user.getUserName());
        } else {
            commentBuilder.append(wfTask.getClaimUser());
        }
        if (StringUtil.isNotBlank(wfTask.getComment())) {
            commentBuilder.append(": ").append(wfTask.getComment());
        }

        // 添加审批意见
        taskService.addComment(wfTask.getTaskId(), task.getProcessInstanceId(), FlowComment.TRANSFER.getType(), commentBuilder.toString());
        // 设置拥有者为当前登录人
        taskService.setOwner(wfTask.getTaskId(), UserContextHolder.getUserId());
        // 转办任务
        taskService.setAssignee(wfTask.getTaskId(), wfTask.getClaimUser());
        // 设置任务节点名称
        wfTask.setTaskName(task.getName());
        // 处理抄送用户
        if (!wfCopyService.makeCopy(wfTask)) {
            throw new RuntimeException("抄送任务失败");
        }
    }

    /**
     * 取消申请
     */
    public void cancelTask(WfTask wfTask) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(wfTask.getProcInsId()).list();
        if (CollectionUtil.isAnyEmpty(task)) {
            throw new RuntimeException("流程未启动或已执行完成，取消申请失败");
        }

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(wfTask.getProcInsId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (Objects.nonNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollectionUtils.isNotEmpty(endNodes)) {
                Authentication.setAuthenticatedUserId(UserContextHolder.getUserId());
                task.stream().forEach(task_ ->{
                    taskService.setAssignee(task_.getId(), UserContextHolder.getUserId());
                    taskService.addComment(task_.getId(), processInstance.getProcessInstanceId(), FlowComment.CANCEL.getType(),
                            (ObjectUtil.isNull(wfTask.getComment()) || StringUtil.isBlank(wfTask.getComment()))
                                    ? "取消申请" : wfTask.getComment());
                });
                // 获取当前流程最后一个节点
                String endId = endNodes.get(0).getId();
                List<Execution> executions = runtimeService.createExecutionQuery()
                        .parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));

                // 变更流程为已结束状态
                runtimeService.createChangeActivityStateBuilder()
                        .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            }
        }
    }

    /**
     * 撤回流程
     */
    @Deprecated
    public void revokeTask(WfTask wfTask) {
        Task task = taskService.createTaskQuery().processInstanceId(wfTask.getProcInsId()).singleResult();
        if (ObjectUtil.isEmpty(task)) {
            throw new ServerException(ExceptionConstants.PROCESS_CANNOT_BACK, null);
        }

        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByTaskCreateTime()
                .asc()
                .list();
        String myTaskId = null;
        HistoricTaskInstance myTask = null;
        for (HistoricTaskInstance hti : htiList) {
            if (UserContextHolder.getUserId().equals(hti.getAssignee())) {
                myTaskId = hti.getId();
                myTask = hti;
                break;
            }
        }
        if (null == myTaskId) {
            throw new RuntimeException("该任务非当前用户提交，无法撤回");
        }

        String processDefinitionId = myTask.getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //变量
        String myActivityId = null;
        List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId()).finished().list();
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }
        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows());
    }

    /**
     * 删除任务
     */
    public void deleteTask(String taskId, String reason) {
        taskService.deleteTask(taskId, reason);
    }

    /**
     * 删除流程实例
     *
     * @param instanceId   流程实例ID
     * @param deleteReason 删除原因
     */
    public void deleteInstance(String instanceId, String deleteReason) {
        // 查询历史数据
        HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId);
        if (null != historicProcessInstance.getEndTime()) {
            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            return;
        }
        // 删除流程实例
        runtimeService.deleteProcessInstance(instanceId, deleteReason);
        // 删除历史流程实例
        historyService.deleteHistoricProcessInstance(instanceId);
    }

    public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(historicProcessInstance)) {
            throw new FlowableObjectNotFoundException(I18nUtil.message(ExceptionConstants.NOT_EXIST, "流程实例-" + processInstanceId));
        }
        return historicProcessInstance;
    }

    /**
     * 获取流程过程图
     */
    public InputStream diagram(String processId) {
        String processDefinitionId;
        // 获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (Objects.isNull(processInstance)) {// 如果流程已经结束，则得到结束节点
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }

        // 获得活动的节点
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        // 高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
                configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
    }
}
